<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>The source code</title>
  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  <style type="text/css">
    .highlight { display: block; background-color: #ddd; }
  </style>
  <script type="text/javascript">
    function highlight() {
      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
    }
  </script>
</head>
<body onload="prettyPrint(); highlight();">
  <pre class="prettyprint lang-js"><span id='global-property-S-'>/**
</span> * @fileOverview 观察者模式实现事件
 * @ignore
 */

var $ = require(&#39;jquery&#39;);

var BUI = require(&#39;./util&#39;),
  ArrayUtil = require(&#39;./array&#39;);
<span id='BUI-Observable-Callbacks'>/**
</span> * @private
 * @class BUI.Observable.Callbacks
 * jquery 1.7 时存在 $.Callbacks,但是fireWith的返回结果是$.Callbacks 对象，
 * 而我们想要的效果是：当其中有一个函数返回为false时，阻止后面的执行，并返回false
 */
var Callbacks = function() {
  this._init();
};

BUI.augment(Callbacks, {

  _functions: null,

  _init: function() {
    var _self = this;

    _self._functions = [];
  },
<span id='BUI-Observable-Callbacks-method-add'>  /**
</span>   * 添加回调函数
   * @param {Function} fn 回调函数
   */
  add: function(fn) {
    this._functions.push(fn);
  },
<span id='BUI-Observable-Callbacks-method-remove'>  /**
</span>   * 移除回调函数
   * @param  {Function} fn 回调函数
   */
  remove: function(fn) {
    var functions = this._functions;
    index = ArrayUtil.indexOf(fn, functions);
    if (index &gt;= 0) {
      functions.splice(index, 1);
    }
  },
<span id='BUI-Observable-Callbacks-method-empty'>  /**
</span>   * 清空事件
   */
  empty: function() {
    var length = this._functions.length; //ie6,7下，必须指定需要删除的数量
    this._functions.splice(0, length);
  },
<span id='BUI-Observable-Callbacks-method-pause'>  /**
</span>   * 暂停事件
   */
  pause: function() {
    this._paused = true;
  },
<span id='BUI-Observable-Callbacks-method-resume'>  /**
</span>   * 唤醒事件
   */
  resume: function() {
    this._paused = false;
  },
<span id='BUI-Observable-Callbacks-method-fireWith'>  /**
</span>   * 触发回调
   * @param  {Object} scope 上下文
   * @param  {Array} args  回调函数的参数
   * @return {Boolean|undefined} 当其中有一个函数返回为false时，阻止后面的执行，并返回false
   */
  fireWith: function(scope, args) {
    var _self = this,
      rst;
    if (_self._paused) {
      return;
    }
    BUI.each(_self._functions, function(fn) {
      rst = fn.apply(scope, args);
      if (rst === false) {
        return false;
      }
    });
    return rst;
  }
});

function getCallbacks() {
  return new Callbacks();
}
<span id='BUI-Observable'>/**
</span> * 支持事件的对象，参考观察者模式
 *  - 此类提供事件绑定
 *  - 提供事件冒泡机制
 *
 * &lt;pre&gt;&lt;code&gt;
 *   var control = new Control();
 *   control.on(&#39;click&#39;,function(ev){
 *
 *   });
 *
 *   control.off();  //移除所有事件
 * &lt;/code&gt;&lt;/pre&gt;
 * @class BUI.Observable
 * @abstract
 * @param {Object} config 配置项键值对
 */
var Observable = function(config) {
  this._events = [];
  this._eventMap = {};
  this._bubblesEvents = [];
  this._initEvents(config);
};

BUI.augment(Observable, {

<span id='BUI-Observable-cfg-listeners'>  /**
</span>   * @cfg {Object} listeners
   *  初始化事件,快速注册事件
   *  &lt;pre&gt;&lt;code&gt;
   *    var list = new BUI.List.SimpleList({
   *      listeners : {
   *        itemclick : function(ev){},
   *        itemrendered : function(ev){}
   *      },
   *      items : []
   *    });
   *    list.render();
   *  &lt;/code&gt;&lt;/pre&gt;
   */

<span id='BUI-Observable-cfg-handler'>  /**
</span>   * @cfg {Function} handler
   * 点击事件的处理函数，快速配置点击事件而不需要写listeners属性
   * &lt;pre&gt;&lt;code&gt;
   *    var list = new BUI.List.SimpleList({
   *      handler : function(ev){} //click 事件
   *    });
   *    list.render();
   *  &lt;/code&gt;&lt;/pre&gt;
   */

<span id='BUI-Observable-property-_events'>  /**
</span>   * 支持的事件名列表
   * @private
   */
  _events: [],

<span id='BUI-Observable-property-_eventMap'>  /**
</span>   * 绑定的事件
   * @private
   */
  _eventMap: {},

  _bubblesEvents: [],

  _bubbleTarget: null,

  //获取回调集合
  _getCallbacks: function(eventType) {
    var _self = this,
      eventMap = _self._eventMap;
    return eventMap[eventType];
  },
  //初始化事件列表
  _initEvents: function(config) {
    var _self = this,
      listeners = null;

    if (!config) {
      return;
    }
    listeners = config.listeners || {};
    if (config.handler) {
      listeners.click = config.handler;
    }
    if (listeners) {
      for (var name in listeners) {
        if (listeners.hasOwnProperty(name)) {
          _self.on(name, listeners[name]);
        }
      };
    }
  },
  //事件是否支持冒泡
  _isBubbles: function(eventType) {
    return ArrayUtil.indexOf(eventType, this._bubblesEvents) &gt;= 0;
  },
<span id='BUI-Observable-method-addTarget'>  /**
</span>   * 添加冒泡的对象
   * @protected
   * @param {Object} target  冒泡的事件源
   */
  addTarget: function(target) {
    this._bubbleTarget = target;
  },
<span id='BUI-Observable-method-addEvents'>  /**
</span>   * 添加支持的事件
   * @protected
   * @param {String|String[]} events 事件
   */
  addEvents: function(events) {
    var _self = this,
      existEvents = _self._events,
      eventMap = _self._eventMap;

    function addEvent(eventType) {
      if (ArrayUtil.indexOf(eventType, existEvents) === -1) {
        eventMap[eventType] = getCallbacks();
        existEvents.push(eventType);
      }
    }
    if (BUI.isArray(events)) {
      BUI.each(events, function(eventType) {
        addEvent(eventType);
      });
    } else {
      addEvent(events);
    }
  },
<span id='BUI-Observable-method-clearListeners'>  /**
</span>   * 移除所有绑定的事件
   * @protected
   */
  clearListeners: function() {
    var _self = this,
      eventMap = _self._eventMap;
    for (var name in eventMap) {
      if (eventMap.hasOwnProperty(name)) {
        eventMap[name].empty();
      }
    }
  },
<span id='BUI-Observable-method-fire'>  /**
</span>   * 触发事件
   * &lt;pre&gt;&lt;code&gt;
   *   //绑定事件
   *   list.on(&#39;itemclick&#39;,function(ev){
   *     alert(&#39;21&#39;);
   *   });
   *   //触发事件
   *   list.fire(&#39;itemclick&#39;);
   * &lt;/code&gt;&lt;/pre&gt;
   * @param  {String} eventType 事件类型
   * @param  {Object} eventData 事件触发时传递的数据
   * @return {Boolean|undefined}  如果其中一个事件处理器返回 false , 则返回 false, 否则返回最后一个事件处理器的返回值
   */
  fire: function(eventType, eventData) {
    var _self = this,
      callbacks = _self._getCallbacks(eventType),
      args = $.makeArray(arguments),
      result;
    if (!eventData) {
      eventData = {};
      args.push(eventData);
    }
    if (!eventData.target) {
      eventData.target = _self;
    }
    if (callbacks) {
      result = callbacks.fireWith(_self, Array.prototype.slice.call(args, 1));
    }
    if (_self._isBubbles(eventType)) {
      var bubbleTarget = _self._bubbleTarget;
      if (bubbleTarget &amp;&amp; bubbleTarget.fire) {
        bubbleTarget.fire(eventType, eventData);
      }
    }
    return result;
  },
<span id='BUI-Observable-method-pauseEvent'>  /**
</span>   * 暂停事件的执行
   * &lt;pre&gt;&lt;code&gt;
   *  list.pauseEvent(&#39;itemclick&#39;);
   * &lt;/code&gt;&lt;/pre&gt;
   * @param  {String} eventType 事件类型
   */
  pauseEvent: function(eventType) {
    var _self = this,
      callbacks = _self._getCallbacks(eventType);
    callbacks &amp;&amp; callbacks.pause();
  },
<span id='BUI-Observable-method-resumeEvent'>  /**
</span>   * 唤醒事件
   * &lt;pre&gt;&lt;code&gt;
   *  list.resumeEvent(&#39;itemclick&#39;);
   * &lt;/code&gt;&lt;/pre&gt;
   * @param  {String} eventType 事件类型
   */
  resumeEvent: function(eventType) {
    var _self = this,
      callbacks = _self._getCallbacks(eventType);
    callbacks &amp;&amp; callbacks.resume();
  },
<span id='BUI-Observable-method-on'>  /**
</span>   * 添加绑定事件
   * &lt;pre&gt;&lt;code&gt;
   *   //绑定单个事件
   *   list.on(&#39;itemclick&#39;,function(ev){
   *     alert(&#39;21&#39;);
   *   });
   *   //绑定多个事件
   *   list.on(&#39;itemrendered itemupdated&#39;,function(){
   *     //列表项创建、更新时触发操作
   *   });
   * &lt;/code&gt;&lt;/pre&gt;
   * @param  {String}   eventType 事件类型
   * @param  {Function} fn        回调函数
   */
  on: function(eventType, fn) {
    //一次监听多个事件
    var arr = eventType.split(&#39; &#39;),
      _self = this,
      callbacks = null;
    if (arr.length &gt; 1) {
      BUI.each(arr, function(name) {
        _self.on(name, fn);
      });
    } else {
      callbacks = _self._getCallbacks(eventType);
      if (callbacks) {
        callbacks.add(fn);
      } else {
        _self.addEvents(eventType);
        _self.on(eventType, fn);
      }
    }
    return _self;
  },
<span id='BUI-Observable-method-off'>  /**
</span>   * 移除绑定的事件
   * &lt;pre&gt;&lt;code&gt;
   *  //移除所有事件
   *  list.off();
   *
   *  //移除特定事件
   *  function callback(ev){}
   *  list.on(&#39;click&#39;,callback);
   *
   *  list.off(&#39;click&#39;,callback);//需要保存回调函数的引用
   *
   * &lt;/code&gt;&lt;/pre&gt;
   * @param  {String}   eventType 事件类型
   * @param  {Function} fn        回调函数
   */
  off: function(eventType, fn) {
    if (!eventType &amp;&amp; !fn) {
      this.clearListeners();
      return this;
    }
    var _self = this,
      callbacks = _self._getCallbacks(eventType);
    if (callbacks) {
      if (fn) {
        callbacks.remove(fn);
      } else {
        callbacks.empty();
      }

    }
    return _self;
  },
<span id='BUI-Observable-method-publish'>  /**
</span>   * 配置事件是否允许冒泡
   * @protected
   * @param  {String} eventType 支持冒泡的事件
   * @param  {Object} cfg 配置项
   * @param {Boolean} cfg.bubbles 是否支持冒泡
   */
  publish: function(eventType, cfg) {
    var _self = this,
      bubblesEvents = _self._bubblesEvents;

    if (cfg.bubbles) {
      if (BUI.Array.indexOf(eventType, bubblesEvents) === -1) {
        bubblesEvents.push(eventType);
      }
    } else {
      var index = BUI.Array.indexOf(eventType, bubblesEvents);
      if (index !== -1) {
        bubblesEvents.splice(index, 1);
      }
    }
  }
});

module.exports = Observable;
</pre>
</body>
</html>
